/*
 * Canvas Game Library
 * Module: Collision
 * 
 * Copyright Chainsawrus Wreaks 2011
 * All rights reserved.
 */

/**
 * @fileOverview A module that adds basic collision detection and response for 2d games, this can be
 * developed into a simple 2d physics engine in the future!
 */

/**
 * Collision module namespace
 * @namespace CGL.collision
 */

CGL.collision = {};

CGL.collision.geom = {
	Point : function(dx, dy) {
		var point = {
			x : dx || 0.0,
			y : dy || 0.0,
			
			pos : function(dx, dy) {
				this.x = dx;
				this.y = dy;
			},
			
			transform : function(dx, dy) {
				this.x += dx;
				this.y += dy;
			},
			
			draw : function(ctx) {
				ctx.beginPath();
				ctx.arc(this.x, this.y, 1, 2*Math.PI, false);
				ctx.fill();
			}
		};
		return point;
	},

	Line : function(dx1, dy1, dx2, dy2) {
		var line = {
			point1 : new CGL.collision.geom.Point(dx1, dy1),
			point2 : new CGL.collision.geom.Point(dx2, dy2),
			
			pos : function(dx1, dy1, dx2, dy2) {
				this.point1.pos(dx1, dy1);
				this.point2.pos(dx2, dy2);
			},
			
			posStart : function(dx, dy) {
				this.point1.pos(dx, dy);
			},
			
			posEnd : function(dx, dy) {
				this.point2.pos(dx, dy);
			},
		
			draw : function(ctx) {
				ctx.beginPath();
				ctx.moveTo(this.point1.x, this.point1.y);
				ctx.lineTo(this.point2.x, this.point2.y);
				ctx.stroke();
			}
		};
		return line;
	},
	
	Rectangle : function(dx, dy, dwidth, dheight) {
		var rectangle = {
			x : dx || 0.0,
			y : dy || 0.0,
			width : dwidth || 1.0,
			height : dheight || 1.0,
			
			pos : function(dx, dy) {
				this.x = dx;
				this.y = dy;
			},
		
			draw : function(ctx) {
				ctx.strokeRect(this.x, this.y, this.width, this.height);
			}
		};
		return rectangle;
	},
	
	Rhomb : function(dx, dy, dwidth, dheight) {
		var rhomb = {
			x : dx,
			y : dy,
			width : dwidth,
			height : dheight,
			
			pos : function(dx, dy) {
				this.x = dx;
				this.y = dy;
			},
			
			transform : function(dx, dy) {
				this.x += dx;
				this.y += dy;
			},
			
			draw : function(ctx) {
				ctx.beginPath();
				ctx.moveTo(this.x, this.y + this.height/2);
				ctx.lineTo(this.x + this.width/2, this.y);
				ctx.lineTo(this.x + this.width, this.y + this.height/2);
				ctx.lineTo(this.x + this.width/2, this.y + this.height);
				ctx.lineTo(this.x, this.y + this.height/2);
				ctx.stroke();
			}
		};
		return rhomb;
	},
	
	Circle : function(dx, dy, radi) {
		var circle = {
			center : new CGL.collision.geom.Point(dx, dy),
			radius : radi || 1.0,
			
			pos : function(dx, dy) {
				circle.center.pos(dx, dy);
			},
		
			draw : function(ctx) {
				ctx.beginPath();
				ctx.arc(circle.center.x, circle.center.y, circle.radius, 2*Math.PI, false);
				ctx.stroke();
			}
		};
		return circle;
	},
	
	AABB : function(cx, cy, wr, hr) {
		var aabb = {
			center : new CGL.collision.geom.Point(cx, cy),
			wradius : wr || 1.0,
			hradius : hr || 1.0,
			
			pos : function(dx, dy) {
				aabb.center.pos(dx, dy);
			},
		
			draw : function(ctx) {
				ctx.strokeRect(aabb.center.x - aabb.wradius, aabb.center.y - aabb.hradius, aabb.wradius*2, aabb.hradius*2);
			}
		};
		return aabb;
	},
	
	Vector2 : function(vx, vy) {
		var Vector2 = {
			x : vx || 0.0,
			y : vy || 0.0,
			
			interpolate : function(a_vec, b_vec, a_f){
				return a_vec.mul(a_f).add(b_vec.mul(1-a_f));	
			},
			
			mul : function(other){
				return new CGL.collision.geom.Vector2(Vector2.x * other.x, Vector2.y * other.y);
			},
			
			mulScale : function(scale) {
				Vector2.x *= scale;
				Vector2.y *= scale;
			},
			
			sub : function(other) {
				return new CGL.collision.geom.Vector2(Vector2.x -= other.x, Vector2.y -= other.y);
			},
			
			add : function(other) {
				return new CGL.collision.geom.Vector2(Vector2.x += other.x, Vector2.y += other.y);
			},
			
			div : function(other) {
				Vector2.x /= other.x;
				Vector2.y /= other.x;
			},
			
			divScale : function(scale) {
				Vector2.x /= scale;
				Vector2.y /= scale;
			},
			
			scale : function(scale){
				return new CGL.collision.geom.Vector2(Vector2.x *= scale, Vector2.y *= scale);
			},
			
			dot : function(other) {
				return ((Vector2.x * other.x) + (Vector2.y * other.y));
			},
			
			cross : function(other) {
				return ((Vector2.x*other.y) - (other.x*Vector2.y));
			},
			
			neg : function() {
				return new CGL.collision.geom.Vector2(-Vector2.x, -Vector2.y);
			},
			
			getAngle : function() {
				var x = Vector2.x;
				var y = Vector2.y;
				
				return Math.atan2(-y, x);
			},
			
			getA : function() {
				var x = Vector2.x;
				var y = Vector2.y;
				
				return Math.atan2(x, -y);
			},
			
			rotate : function(degre) {
				var r = CGL.collision.geom.degreToRadian(degre);
				var rx, ry;
				rx = Math.cos(r)*Vector2.x - Math.sin(r)*Vector2.y;
				ry = Math.sin(r)*Vector2.x + Math.cos(r)*Vector2.y;
				
				return new CGL.collision.geom.Vector2(rx, ry);
			},
			
			rotatePivot : function(degre, px, py) {
				var r = CGL.collision.geom.degreToRadian(degre);
				var rx, ry;
				rx = Vector2.x;
				ry = Vector2.y;
				
				rx -= px;
				ry -= py;
				
				Vector2.x = Math.cos(r)*rx - Math.sin(r)*ry;
				Vector2.y = Math.sin(r)*rx + Math.cos(r)*ry;
				
				Vector2.x += px;
				Vector2.y += py;
				
				return new CGL.collision.geom.Vector2(Vector2.x, Vector2.y);
			},
			
			transform : function(x, y) {
				return new CGL.collision.geom.Vector2(Vector2.x + x, Vector2.y + y);
			},
			
			projection : function(other) {
				var dot = Vector2.dot(other);
				var length = Vector2.lengthSquared();
				return new CGL.collision.geom.Vector2((dot/length)*Vector2.x, (dot/length)*Vector2.y);
			},
			
			normalize : function() {
				var l = Vector2.length();
				
				if(l == 0){
					return;
				}
				
				Vector2.x /= l;
				Vector2.y /= l;
			},
			
			lengthSquared : function() {
				return (Vector2.x * Vector2.x) + (Vector2.y * Vector2.y);
			},
			
			length : function() {
				return Math.sqrt(Vector2.lengthSquared());
			},
		
			toString : function() {
				return "[Vector (" + Vector2.x + ", " + Vector2.y + ")]";
			},
			
			copy : function(other) {
				Vector2.x = other.x;
				Vector2.y = other.y;
			},
			
			draw : function(ctx) {
				ctx.beginPath();
				ctx.arc(Vector2.x, Vector2.y, 2, 0, Math.PI*2, true); // Outer circle
				ctx.fill();
			},
			
			drawDir : function(ctx, x, y) {
				ctx.beginPath();
				ctx.moveTo(x, y);
				ctx.lineTo(x+Vector2.x, y+Vector2.y);
				ctx.closePath();
				ctx.stroke();
			},
			
			drawArrow : function(ctx, x, y) {
				var temp = new CGL.collision.geom.Vector2(Vector2.x, Vector2.y);
				
				ctx.beginPath();
				ctx.moveTo(x, y);
				ctx.lineTo(x+Vector2.x, y+Vector2.y);
				ctx.closePath();
				ctx.stroke();

				temp.normalize();
				temp = temp.rotate(30);
				
				ctx.beginPath();
				ctx.moveTo(x+Vector2.x, y+Vector2.y);
				ctx.lineTo((x+Vector2.x)-(temp.x*10), (y+Vector2.y)-(temp.y*10));
				ctx.closePath();
				ctx.stroke();
				
				temp = temp.rotate(-60);

				ctx.beginPath();
				ctx.moveTo(x+Vector2.x, y+Vector2.y);
				ctx.lineTo((x+Vector2.x)-(temp.x*10), (y+Vector2.y)-(temp.y*10));
				ctx.closePath();
				ctx.stroke();
			},
			
			drawDest : function(ctx, x, y) {
				ctx.beginPath();
				ctx.arc(x+Vector2.x, y+Vector2.y, 2, 0, Math.PI*2, true); // Outer circle
				ctx.fill();
			}
		};
		return Vector2;
	},
	
	getNormal : function(vx, vy) {
		var n = new CGL.collision.geom.Vector2(vy.x, vy.y);
		n.sub(vx);
		
		n = new CGL.collision.geom.Vector2(n.y, -n.x);
		n.normalize();
		
		return n;
	},
	
	Cross : function(point1, point2) {
		return point1.y * point2.x - point1.x * point2.y;
	},
	
	radianToDegre : function(radian) {
		return radian * (180/Math.PI);
	},
	
	degreToRadian : function(degre) {
		return degre * (Math.PI/180);
	},
	
	getDistance : function(point_a, point_b) {
		var vecA = new TD.math.Vector2();
		var vecB = new TD.math.Vector2();
		
		vecA.copy(point_a);
		vecB.copy(point_b);
		
		vecB = vecB.sub(vecA);
		//CGL.util.printLn("Distance: " + vecB.length())
		return vecB.length();
	}
};

CGL.collision.detect = {
		
	PointTriangle : function(point, t1, t2, t3) {
		var vPoint, vT1, vT2, vT3;
		
		vPoint = new CGL.collision.geom.Vector2(point.x, point.y);
		vT1 = new CGL.collision.geom.Vector2(t1.x, t1.y);
		vT2 = new CGL.collision.geom.Vector2(t2.x, t2.y);
		vT3 = new CGL.collision.geom.Vector2(t3.x, t3.y);
		
		if(CGL.collision.geom.Cross(vPoint.sub(vT1), vT1.sub(vT2)) < 0.0) { return false; }
		
		vPoint = new CGL.collision.geom.Vector2(point.x, point.y);
		vT1 = new CGL.collision.geom.Vector2(t1.x, t1.y);
		vT2 = new CGL.collision.geom.Vector2(t2.x, t2.y);
		vT3 = new CGL.collision.geom.Vector2(t3.x, t3.y);
		
		if(CGL.collision.geom.Cross(vPoint.sub(vT2), vT2.sub(vT3)) < 0.0) { return false; }
		
		vPoint = new CGL.collision.geom.Vector2(point.x, point.y);
		vT1 = new CGL.collision.geom.Vector2(t1.x, t1.y);
		vT2 = new CGL.collision.geom.Vector2(t2.x, t2.y);
		vT3 = new CGL.collision.geom.Vector2(t3.x, t3.y);
		
		if(CGL.collision.geom.Cross(vPoint.sub(vT3), vT3.sub(vT1)) < 0.0) { return false; }
		
		return true;
	},
	
	PointRhomb : function(point, rhomb) {
		var vPoint, p1, p2, p3, p4;
		
		vPoint = new CGL.collision.geom.Vector2(point.x, point.y);
		p1 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width/2, rhomb.y);
		p2 = new CGL.collision.geom.Vector2(rhomb.x, rhomb.y + rhomb.height/2);
		p3 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width/2, rhomb.y + rhomb.height);
		p4 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width, rhomb.y + rhomb.height/2);
		
		if(CGL.collision.geom.Cross(vPoint.sub(p1), p1.sub(p2)) < 0.0) { return false; }
		
		vPoint = new CGL.collision.geom.Vector2(point.x, point.y);
		p1 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width/2, rhomb.y);
		p2 = new CGL.collision.geom.Vector2(rhomb.x, rhomb.y + rhomb.height/2);
		p3 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width/2, rhomb.y + rhomb.height);
		p4 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width, rhomb.y + rhomb.height/2);
		
		if(CGL.collision.geom.Cross(vPoint.sub(p2), p2.sub(p3)) < 0.0) { return false; }
		
		vPoint = new CGL.collision.geom.Vector2(point.x, point.y);
		p1 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width/2, rhomb.y);
		p2 = new CGL.collision.geom.Vector2(rhomb.x, rhomb.y + rhomb.height/2);
		p3 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width/2, rhomb.y + rhomb.height);
		p4 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width, rhomb.y + rhomb.height/2);
		
		if(CGL.collision.geom.Cross(vPoint.sub(p3), p3.sub(p4)) < 0.0) { return false; }
		
		vPoint = new CGL.collision.geom.Vector2(point.x, point.y);
		p1 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width/2, rhomb.y);
		p2 = new CGL.collision.geom.Vector2(rhomb.x, rhomb.y + rhomb.height/2);
		p3 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width/2, rhomb.y + rhomb.height);
		p4 = new CGL.collision.geom.Vector2(rhomb.x + rhomb.width, rhomb.y + rhomb.height/2);
		
		if(CGL.collision.geom.Cross(vPoint.sub(p4), p4.sub(p1)) < 0.0) { return false; }
		
		return true;
	},
	
	PointRectangle : function(point, rectangle) {
		return (point.x > rectangle.x && point.x < rectangle.x + rectangle.width && 
				point.y > rectangle.y && point.y < rectangle.y + rectangle.height);
	},
	
	PointCircle : function(point, circle) {
		var vPoint = new CGL.collision.geom.Vector2(point.x, point.y);
		var vCircle = new CGL.collision.geom.Vector2(circle.center.x, circle.center.y);
		
		vPoint.sub(vCircle);
		
		return (vPoint.length() < circle.radius);
	},
	
	LineLine : function(line1, line2) {
		var b, d, bDotDPerp, c, t, u;
		
		b = new CGL.collision.geom.Vector2(line1.point2.x - line1.point1.x, line1.point2.y - line1.point1.y);
		d = new CGL.collision.geom.Vector2(line2.point2.x - line2.point1.x, line2.point2.y - line2.point1.y);
		bDotDPerp = b.x * d.y - b.y * d.x;
		
		if(bDotDPerp == 0) { return false; }
		
		c = new CGL.collision.geom.Vector2(line2.point1.x - line1.point1.x, line2.point1.y - line1.point1.y);
		t = (c.x * d.y - c.y * d.x) / bDotDPerp;
		if(t < 0 || t > 1) { return false; }
		
		u = (c.x * b.y - c.y * b.x) / bDotDPerp;
		if(u < 0 || u > 1) { return false; }
		
		return true;
	},
	
	LineAabb : function(line, aabb) {
		var testLine, pt1, pt2, pt3, pt4;
		pt1 = new CGL.collision.geom.Point(aabb.center.x - aabb.wradius, aabb.center.y - aabb.hradius);
		pt2 = new CGL.collision.geom.Point(aabb.center.x - aabb.wradius, aabb.center.y + aabb.hradius);
		pt3 = new CGL.collision.geom.Point(aabb.center.x + aabb.wradius, aabb.center.y + aabb.hradius);
		pt4 = new CGL.collision.geom.Point(aabb.center.x + aabb.wradius, aabb.center.y - aabb.hradius);
		
		testLine = new CGL.collision.geom.Line(pt1.x, pt1.y, pt2.x, pt2.y);
		if(CGL.collision.detect.LineLine(testLine, line)) { return true; }
		
		testLine = new CGL.collision.geom.Line(pt2.x, pt2.y, pt3.x, pt3.y);
		if(CGL.collision.detect.LineLine(testLine, line)) { return true; }
		
		testLine = new CGL.collision.geom.Line(pt3.x, pt3.y, pt4.x, pt4.y);
		if(CGL.collision.detect.LineLine(testLine, line)) { return true; }
		
		testLine = new CGL.collision.geom.Line(pt4.x, pt4.y, pt1.x, pt1.y);
		if(CGL.collision.detect.LineLine(testLine, line)) { return true; }
		
		return false;
	},

	RectangleRectangle : function(rectangle1, rectangle2) {
		return (rectangle1.x + rectangle1.width >= rectangle2.x && 
				rectangle1.y + rectangle1.height >= rectangle2.y && 
				rectangle1.x <= rectangle2.x + rectangle2.width && 
				rectangle1.y <= rectangle2.y + rectangle2.height);
	},
	
	CircleCircle : function(circle1, circle2) {
		var vCircle = new CGL.collision.geom.Vector2(circle1.center.x, circle1.center.y);
		var vCircle2 = new CGL.collision.geom.Vector2(circle2.center.x, circle2.center.y);
		
		vCircle.sub(vCircle2);
		
		return (vCircle.length() < circle1.radius + circle2.radius);
	},
	
	AabbAabb : function(aabb1, aabb2) {
		if(Math.abs(aabb1.center.x - aabb2.center.x) > (aabb1.wradius + aabb2.wradius))
		{
			return false;
		}
		else if(Math.abs(aabb1.center.y - aabb2.center.y) > (aabb1.hradius + aabb2.hradius))
		{
			return false;
		}
		else
		{
			return true;
		}
	},
	
	AabbCircle : function(aabb, circle) {
		if(Math.abs(aabb.center.x - circle.center.x) > (aabb.wradius + circle.radius))
		{
			return false;
		}
		else if(Math.abs(aabb.center.y - circle.center.y) > (aabb.hradius + circle.radius))
		{
			return false;
		}
		else
		{
			if(circle.center.x < aabb.center.x - aabb.wradius)
			{
				if(circle.center.y < aabb.center.y - aabb.hradius)
				{
					return CGL.collision.detect.PointCircle(
							new CGL.collision.geom.Point(aabb.center.x - aabb.wradius, aabb.center.y - aabb.hradius), circle);
				}
				else if(circle.center.y > aabb.center.y + aabb.hradius)
				{
					return CGL.collision.detect.PointCircle(
							new CGL.collision.geom.Point(aabb.center.x - aabb.wradius, aabb.center.y + aabb.hradius), circle);
				}
			}
			else if(circle.center.x > aabb.center.x + aabb.wradius)
			{
				if(circle.center.y < aabb.center.y - aabb.hradius)
				{
					return CGL.collision.detect.PointCircle(
							new CGL.collision.geom.Point(aabb.center.x + aabb.wradius, aabb.center.y - aabb.hradius), circle);
				}
				else if(circle.center.y > aabb.center.y + aabb.hradius)
				{
					return CGL.collision.detect.PointCircle(
							new CGL.collision.geom.Point(aabb.center.x + aabb.wradius, aabb.center.y + aabb.hradius), circle);
				}
			}
			return true;
		}
	}
};

CGL.collision.response = {
		
};

CGL.collision.strategy = {
		
};